/**
 * 分配来源对象的可枚举属性到目标对象上。 
 * 来源对象的应用规则是从左到右，随后的下一个对象的属性会覆盖上一个对象的属性。
 * @param target - 要合并的对象。
 * @param sources - 要合并的对象。
 * @returns 返回合并后的对象。
 * @example
 * function Foo() {
 *   this.a = 1;
 * }
 * function Bar() {
 *   this.c = 3;
 * }
 * Foo.prototype.b = 2;
 * Bar.prototype.d = 4;
 * assign({ 'a': 0 }, new Foo, new Bar);
 * // => { 'a': 1, 'c': 3 }
 */
export function assign<T extends object, U extends object[]>(target: T, ...sources: U): T & U[number] {
    if (target == null) {
        throw new TypeError('Cannot convert undefined or null to object');
    }
    
    // 处理原始值包装对象
    target = Object(target);
    
    for (const source of sources) {
        if (source == null) continue;
        
        // 复制所有可枚举的自有属性
        for (const key in source) {
            if (Object.prototype.hasOwnProperty.call(source, key)) {
                (target as any)[key] = source[key];
            }
        }
        
        // 复制Symbol属性
        const symbols = Object.getOwnPropertySymbols(source);
        for (const symbol of symbols) {
            (target as any)[symbol] = source[symbol];
        }
    }
    
    return target as T & U[number];
}